home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Belgian Amiga Club - ADF Collection
/
BS1 part 65.zip
/
BS1 part 65
/
Delitracker v1.21.adf
/
Developer
/
Programmierung.dok
< prev
next >
Wrap
Text File
|
1992-05-17
|
18KB
|
537 lines
Wie schreibt man externe Player?
© 1992 by Delirium
Datum 15.05.1992
Das externe Player Konzept
DeliTracker unterstützt sog. externe Player, das sind Player die in einem
speziellen Format vorliegen und von DeliTracker nachgeladen werden können.
Ist ein solcher Player geladen worden erkennt und spielt DeliTracker die
Module, die von diesem Player unterstützt werden. Derzeit kann DeliTracker
maximal 64 externe Player nachladen. Dies sollte jedoch erst mal für eine
Weile reichen.
Beim Start von DeliTracker werden alle Player die sich im Verzeichnis
'DeliPlayers' (b.z.w im Configfile gespeicherten Playerpfad) befinden
geladen. Zusätzlich haben Sie jederzeit die Möglichkeit einen weiteren
b.z.w. neueren Player nachzuladen. Diese Player verleihen DeliTracker
seine ungeahnte Flexibilität.
Externe Player sind Executables, d.h. sie werden reloziert. Am Anfang
besitzen sie die charakteristische Playerstruktur. Sie wird mit dem Macro
aus dem Includefile 'misc/deliplayer.i' erzeugt.
Es ist relativ einfache eine Replayroutine an DeliTracker anzupassen.
Das 'einzige' was Sie tun müssen ist ein wenig Interfacecode zu schreiben.
Dies ist aber halb so wild, denn DeliTracker stellt ihnen viele hilfreiche
Routinen zur Verfügung.
1. Grundvoraussetzungen
Um ein neues Soundsystem anzupassen, benötigen Sie zum einen den Sourcecode
oder ein linkbares Objektfile der Replayroutine, zum anderen mindestens ca.
5 Module zum testen der Funktionstüchtigkeit des Players.
2. Schematischer Aufbau eines externen Players
{
Playerheader
TagArray
Interfacecode
Replaycode+Daten
}
3. Funktionen, die der externe Player zur Verfügung stellt
Das PLAYERHEADER Macro generiert den Header, der das File als externen Player
identifiziert. Dieses Macro muß angegeben werden und ganz am Anfang des Players
stehen. Als einziger Parameter ist ein Pointer auf ein Tag Array einzutragen,
in dem alle Funktionen stehen, die der Player DeliTracker zur Verfügung stellt.
Bei allen Funktionsaufrufen außer der Interruptroutine (DTP_Interrupt) enthält a5
die Base. Globale Variablen können dadurch adressiert werden. (Siehe auch
'misc/deliplayer.i'). Da DeliTracker vor Aufruf einer Playerroutine (außer bei
DTP_Interrupt) alle Register sichert, dürfen diese verändert werden.
PLAYERHEADER <tagarray>
tagarray Pointer auf ein Tag Array, das mit TAG_DONE
abgeschlossen sein muß. Tags, bei denen ti_Data NULL
ist, werden ignoriert.
Außerdem können folgende SystemTags verwendet werden:
TAG_DONE
TAG_IGNORE
TAG_MORE
TAG_SKIP
Derzeit existieren folgende Tags:
DTP_CustomPlayer dieser Tag deklariert einen Player als Customplayer
ti_Data darf nicht NULL sein !
DTP_RequestDTVersion damit kann man sicherstellen, daß mindestens eine bestimmte
Version von DeliTracker vorhanden ist. Dieser Tag muß
angegeben werden, wenn bei den DeliTrackerGlobals neue
Funktionspointer hinzugekommen sind und diese vom Player
benutzt werden. ti_Data ist dabei die Playerrevision.
DTP_RequestV37 wenn dieser Tag vorhanden ist, wird der Player nur noch
von der Kick 2.0 Version von DeliTracker geladen.
(d.h. dtg_GadToolsBase ist gültig)
DTP_PlayerVersion Tag, der die Revisionsnummer des Players enthält.
Bei zwei Playern mit dem gleichen Playernamen wird
derjenige mit der größeren Revisionsnummer geladen.
DTP_PlayerName ti_Data enthält den Pointer auf den Namen des Players.
Dieser String kann zwar beliebig lang sein aber zur Zeit
werden nur die ersten 24 Zeichen angezeigt.
Dieser Tag muß existieren !
DTP_Creator Pointer auf den Namen des Autors. Dieser wird im Setup-
fenster im Playerinfofeld angezeigt. Dieser String kann
$A als Zeilenumbruch enthalten.
DTP_Check1 Pointer auf eine Modulerkennungsroutine, die die aufgerufen
wird, wenn 1024 Bytes des Moduls geladen sind. Wird das
Modul erkannt, liefert sie d0=0, ansonsten d0<>0.
DTP_Check2 Pointer auf eine Modulerkennungsroutine, die die aufgerufen
wird, wenn das komplette Modul geladen und evtl. entpackt
ist. Wird das Modul erkannt, liefert sie d0=0, ansonsten
d0<>0.
DTP_ExtLoad Pointer auf eine optionale Laderoutine für Module. Ist
kein Fehler aufgetreten, wird d0=0 zurückgegeben, sonst
d0<>0. Hinweis: Achten Sie darauf, daß im Fehlerfall alle
allocierten Ressourcen (Memory, Locks, ...) wieder frei-
gegeben werden, da dann keine weiteren Playerfunktionen
mehr angesprungen werden.
DTP_Interrupt Pointer auf eine Interruptroutine, die mittels eines
Timerinterrupts standardmäßig alle 1/50 sec aufgerufen
wird. Dies ist die Standardmethode, um mit der richtigen
Abspielgeschwindigkeit auf im PAL/NTSC/Productivity
Videomodus zu spielen. Wenn keine DTP_faster/DTP_slower
Tags angegeben sind, übernimmt DeliTracker dies durch
Verändern der Interruptfrequenz. Dieser Tag kann auch
nicht existieren, dann müssen aber DTP_StartInt/DTP_StopInt
vorhanden sein !
DTP_Stop Pointer auf eine optionale Stoproutine. Wenn dieser Tag
nicht vorhanden ist, verfährt DeliTracker folgendermaßen:
Interrupt stoppen (DTP_StopInt)
Sound Cleanup (DTP_EndSnd)
Song initialisieren (DTP_InitSnd)
Ansonsten hat diese Routine die Aufgabe, einen evtl.
spielenden Song anzuhalten und so zu initialisieren, daß
dieser beim nächsten Starten des Interrupts von Anfang an
zu spielen beginnt.
DTP_Config Pointer auf eine optionale Initialisierungsroutine. Diese
wird nur einmal unmittelbar nach dem Laden des Players
aufgerufen. Mögliche Anwendungen: Laden einer player-
spezifischen Konfigurationsdatei.
DTP_UserConfig Pointer auf eine optionale Initialsierungsroutine. Diese
wird nur dann aufgerufen, wenn der User den Player im
Setupfenster anwählt und das 'Config' Gadget drückt.
Mögliche Anwendungen: Öffnen eines Fensters zum Setzen
weiterer Optionen (Instrumentenpfad, ...) und abspeichern
einer playerspezifischen Konfigurationsdatei.
DTP_SubSongRange Dieser Tag sollte angegeben werden, wenn der Player Multi-
Module unterstützt. ti_Data zeigt dabei auf eine Routine,
die als Returnwert in d0 die minimale und in d1 die maximale
Subsongnummer zurückgeben muß.
DTP_InitPlayer Pointer auf eine optionale Initialsierungsroutine, die
aufgerufen wird, wenn ein Modul erfolgreich geladen wurde.
Tritt kein Fehler auf, liefert sie d0=0, ansonsten d0<>0.
Hier muß die Allocation der Audiokanäle stattfinden !
(DeliTracker stellt dafür eine eigene Routine zur Verfügung)
Falls der Player Multi-Module unterstützt, muß hier
dtg_SndNum(a5) auf die erste Subsongnummer gesetzt werden.
DTP_EndPlayer Pointer auf eine optionale Cleanuproutine, die aufgerufen
wird, wenn das Modul aus dem Speicher entfernt wird. Hier
muß die Freigabe der Audiokanäle stattfinden ! (DeliTracker
stellt dafür eine eigene Routine zur Verfügung)
DTP_InitSound Pointer auf eine optionale Initialsierungsroutine.
Diese muß das Modul initialisieren, so daß beim Starten
des Interrupts der Song von Anfang an zu spielen beginnt.
DTP_EndSound Pointer auf eine optionale Cleanuproutine.
Diese kann z.B. die Lautstärkeregister und die Audio-DMA
rücksetzen.
DTP_StartInt Pointer auf eine Initialsierungsroutine, die existieren
muß, wenn DTP_Interrupt nicht vorhanden ist. In diesem
Fall muß hier der Sound gestartet werden.
DTP_StopInt Pointer auf eine Cleanuproutine, die existieren muß,
wenn DTP_Interrupt nicht vorhanden ist. In diesem Fall
muß hier der Sound gestoppt werden.
DTP_Volume Pointer auf eine Funktion, die die Lautstärke neu setzt.
Die Funktion wird aufgerufen, wenn die Volume neu gesetzt
wird (Slider, ARexx-Kommando) und beim Initialisieren des
Moduls vor DTP_InitSnd. Die Mastervolume steht in
dtg_SndVol(a5). Die Mastervolume ist dabei der maximale
Volumewert. Die effektive Volume errechnet sich also
durch: VOL_eff = ( (MASTERVOLUME*modulevolume) >> 6 ).
Näheres siehe Beispielsourcen.
DTP_Balance Pointer auf eine Funktion, die die Balance neu setzt.
Die Funktion wird aufgerufen, wenn die Balance neu gesetzt
wird (Slider, ARexx-Kommando) und beim Initialisieren des
Moduls vor DTP_InitSnd. Die Balance für die linken Kanäle
steht in dtg_SndLBal(a5), für die rechten Kanäle in
dtg_SndRBal(a5). Hinweis: Alle Player die Balance unterstützen
können auch Volume ! Man verwendet dann die gleiche Routine
zum setzen der Volume&Balance. Die linke Volume errechnet
sich wie folgt: ( ( dtg_Volume(a5) * dtg_SndLBal(a5) ) >> 6 )
Entsprechendes gilt für rechts.
DTP_Faster Pointer auf eine Funktion, die den Abspielvorgang
beschleunigt.
DTP_Slower Pointer auf eine Funktion, die den Abspielvorgang
verlangsamt.
DTP_NextPatt Pointer auf eine Funktion, die den den Patternzeiger um
eins erhöht.
DTP_PrevPatt Pointer auf eine Funktion, die den den Patternzeiger um
eins erniedrigt.
DTP_NextSong Pointer auf eine Funktion, die Subsongnummer auf den den
nächsten Subsong setzt und diesen spielt.
(Falls vorhanden)
DTP_PrevSong Pointer auf eine Funktion, die Subsongnummer auf den den
vorhergehenden Subsong setzt und diesen spielt.
(Falls vorhanden)
4. Funktionen, die DeliTracker dem externen Player zur Verfügung stellt
Eine Funktion wird wie folgt aufgerufen:
move.l dtg_XXX(a5),a0
jsr (a0)
Alle folgenden Funktionen außer dtg_SongEnd verwenden d0/d1/a0/a1 als
Scratchregister. In a5 muß bei allen Aufrufen die Base stehen.
Derzeit existieren folgende Funktionen:
dtg_GetListData
SYNOPSIS
memory size = dtg_GetListData(number)
a0 d0 d0.l
FUNCTION
Liefert Adresse und Länge eines mit LoadFile() geladenen Files.
INPUTS
number - Nummer eines Files beginnend mit 0 für das vom User
selektierte File.
RESULT
memory - ein Pointer auf die Startadresse des Files im Speicher
oder NULL im Fehlerfall.
size - Länge des Files in Bytes bzw. 0 im Fehlerfall.
dtg_LoadFile
SYNOPSIS
success = dtg_LoadFile(name)
FUNCTION
Lädt und entpackt ggf. das angegebene File ins Chip-Memory.
(Hinweis: diese Funktion ergänzt automatisch, falls das File
mit dem angegebenen Namen nicht geöffnet werden konnte '.pp')
INPUTS
name - der Filename steht in einem internen Buffer (seine
Adresse steht in dtg_PathArray)
RESULT
success - alles ok d0.l=0, sonst d0.l<>0.
dtg_CopyDir
SYNOPSIS
dtg_CopyDir()
FUNCTION
Kopiert das Directory des von User angewählten Files an das
Ende des Strings, auf den dtg_PathArray(a5) zeigt.
dtg_CopyFile
SYNOPSIS
dtg_CopyFile()
FUNCTION
Kopiert den Filenamen des von User angewählten Files an das
Ende des Strings, auf den dtg_PathArray(a5) zeigt.
dtg_CopyString
SYNOPSIS
dtg_CopyString(string)
a0
FUNCTION
Kopiert den String, auf den das Register a0 zeigt, an das
Ende des Strings, auf den dtg_PathArray(a5) zeigt.
INPUTS
string - der Pointer auf den anzuhängenden String steht in a0
dtg_AudioAlloc
SYNOPSIS
success = dtg_AudioAlloc()
FUNCTION
Belegt alle Audiokanäle.
RESULT
success - alles ok d0.l=0, sonst d0.l<>0.
dtg_AudioFree
SYNOPSIS
dtg_AudioFree()
FUNCTION
Gibt die mit dtg_AudioAlloc belegten Audiokanäle wieder frei.
dtg_StartInt
SYNOPSIS
dtg_StartInt()
FUNCTION
Startet den Soundinterrupt. (Falls er nicht schon läuft.)
Falls DTP_Interrupt existiert, startet DeliTracker einen
Timerinterrupt, ansonsten wird DTP_StartInt aufgerufen.
dtg_StopInt
SYNOPSIS
dtg_StopInt()
FUNCTION
Stoppt den Soundinterrupt. (Falls er nicht schon angehalten
ist.) Falls DTP_Interrupt existiert, stoppt DeliTracker seinen
Timerinterrupt, ansonsten wird DTP_StopInt aufgerufen.
dtg_SongEnd
SYNOPSIS
dtg_SongEnd()
FUNCTION
Signalisiert DeliTracker, daß das Modul einmal komplett
gespielt wurde. Diese Funktion verändert keine Register
und darf auch von Interrupts aufgerufen werden.
dtg_CutSuffix
SYNOPSIS
dtg_CutSuffix()
FUNCTION
Entfernt am Ende des Strings, auf den dtg_PathArray(a5) zeigt
ggf. die Endung '.pp'
5. Die Modul-Erkennung
Damit DeliTracker die einzelnen Module unterscheiden kann, befindet sich in
jedem Player eine Erkennungsroutine, die auf ein zugehöriges Modul testet.
Diese Routine prüft auf Stellen im Modul, die bei allen Modulen eines Players
gleich sind. So z.B. auf 'M.K.' bei Offset $438 im NoiseTracker-Player.
Bei Sound- und NoiseTracker ist nur das Modul abgespeichert, z.B. bei MarkII
hängt vor jedem Modul noch die Replayroutine. Bei so einem Modul müssen Sie
auf signifikante Assemblerbefehle testen. Ein Vergleich auf ein oder zwei
Sprungbefehle, egal ob bra oder jmp, ist hier nicht ausreichend, da bei
vielen Modulen dieses Typs Sprungtabellen am Anfang stehen und der Player
möglicherweise das falsche Modul erkennen könnte, was in den meisten Fällen
zum Absturz führt.
Der Player MUß GENAU EINE Checkroutine verwenden. Daraus ergeben sich zwei
Grundtypen von Playern.
1. Typ eins Player - in der Regel etwas komplexer
Bei ihnen ist die Check1 Funktion implementiert.
Vorteil: Es lassen sich auch Player realisieren, die das Modul selbst nachladen.
Nachteil: Gepackte Files werden nicht unterstützt.
Dieser Player sollte nur für Härtefälle verwendet werden.
(z.B: FTM, IFF-8SVX Player, der das Sample während dem Spielen nachlädt, ...)
2. Typ zwei Player - die einfachere Variante
Bei ihnen ist die Check2 Funktion implementiert
Vorteil: Das Modul kann gepackt sein, der Player bemerkt davon nichts.
Nachteil: Das Modul ist in jedem Fall im Chip-Memory.
Es sollte im Normalfall dieser Playertyp verwendet werden.
Egal ob Typ eins oder zwei, wenn der Player das Modul erkannt hat, muß er
in d0.l=0 zurückliefern, wenn nicht d0.l<>0.
6. Player-Interrupt
Hier gibt es auch grob eine Einteilung in zwei verschiedene Typen.
1. Player die den DeliTracker Interrupt verwenden
Vorteil: Der Player ist vom Videomodus unabhängig.
Besitzt automatisch die faster/slower Funktion.
Kein Aufwand für den Interrupt (Code + Interruptstruktur).
Ist kompatibel zum serial.device
Nachteil: Der Interrupt kommt nicht synchron zum VBlank. (Dies führt
nur in den seltensten Fällen zu Problemen.)
2. Player die ihren eigenen Interrupt erzeugen.
Vorteil: Es können andere Interruptquellen benutzt werden
(z.B. AudioIRQ).
Nachteil: Erhöhter Aufwand, bei VBlank nicht mehr unabhängig vom
Videomodus.
Wenn ein eigener Timerinterrupt verwendet wird, sollte die CIAB
(wg. OS2.0) und die entsprechenden Resourcefunktionen verwendet werden.
Außerdem ist es sehr sinnvoll die Replayroutine nicht direkt im CIA-B Timer-
Interrupt abspielen zu lassen, sondern im Timerinterrupt einen Soft Interrupt
mittels Cause() zu generieren. In diesem SoftInt kann man dann die eigentliche
Replayroutine ablaufen lassen. So hat man den Vorteil, daß man wesentlich
kompatibler zum serial.device ist. Dies liegt daran, daß SoftInt eine
niedrigere Priorität als der der RBF (Read Buffer Full) Interrupt hat d.h.
erst wird der serielle Port bedient, dann erst die Replayroutine.
Es wird davor gewarnt, direkt in die Interruptvektoren zu schreiben!
Zur Erinnerung: vom Betriebssystem werden die Funktionen AddIntServer()
und SetIntVector() zur Verfügung gestellt !
7. Custom Module
Dies sind keine Module im herkömmlichen Sinn, sondern im wesentlichen
Player, die ein Spezialmodul beinhalten. Mit diesem Format können Sie
auch die ausgefallensten Module mit DeliTracker abspielen. Sollten für
den Player jedoch mehrere Module existieren, ist es in dem Fall ratsam,
einen richtigen Player zu schreiben.
Schematischer Aufbau eines Custom Moduls
{
Playerheader
TagArray
Interfacecode
Replaycode+Daten
MODUL
}
Mit dem Tag DTP_CustomPlayer wird ein Player als Customplayer deklariert
und folgende Tags sind dann bedeutungslos:
- DTP_PlayerVersion
- DTP_PlayerName
- DTP_Creator
- DTP_Check1
- DTP_Check2
- DTP_ExtLoad
- DTP_Config
- DTP_UserConfig
8. Checkliste
Dies ist eine kleine Checkliste für Player/Custommodul Anpassung, die erfüllt
werden muß, damit der Player systemkonform ist und einwandfrei funktioniert.
[ ] ist die Checkroutine präzise genug ?
[ ] werden die Audiokanäle richtig belegt und freigegeben ?
[ ] wird aller allocierter Speicher freigegeben ?
[ ] werden alle Locks wieder freigegeben ?
[ ] enforcer und mungwall - Test bestanden ?
[ ] werden alle möglichen Fehler korrekt abgefangen ?
[ ] wurde der Player mit 1.2, 1.3 und 2.0 getestet ?
[ ] spielt der Player in allen Videomodi korrekt ?
9. Diverses
Der Player sollte den Zustand der LED unbeeinflußt lassen, da im Setupwindow
ein entsprechende Funktion existiert.
Problemecke
Symptom mögliche Ursache Beseitigung
Absturz a5 überschrieben
Register nicht gesichert
falsche oder zu späte Initialisierung
Modul zu neu für den Player präziserer Check
klingt schräg Audiodaten nicht im Chipmem Player ins Chipmem
falsche Initialisierung
Modul zu neu für den Player präziserer Check
Player benötigt bestimmte Werte in zusätzliche Initialisierung
einigen Registern
unverträglicher Videomodus
kein Ton Audio-DMA abgeschaltet
bei >68000 Player schreibt direkt auf Betriebssystemfuntionen
Interruptvektoren benutzen
fast keine fehlerhaftes Interrupt-Handling bei VBlank-IRQ:
freie CPU-Zeit Z-Flag muß am Ende gesetzt sein!
lahmes System 68000/020/030 zu langsam 60040 kaufen ;-)